package Presenter;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JOptionPane;
import javax.swing.Timer;

import Model.Game;
import Model.Mule;
import Model.Player;
import Model.Tile;
import View.Town.PubView;

/**
 * Turn class which runs each turn
 * 
 * @author Philip & Josh
 * 
 */
public class Turn {

	private int elapsedTime;
	private int currentPlayer;
	private Timer turnTimer;
	private Game g;
	private MainGameScreen gameScreen;
	private boolean landBought;

	/**
	 * Constructs turn. The actionListener sees an event every 1000ms, updating
	 * the turn time, which is initially set to 0.
	 */
	public Turn(final Game g, final MainGameScreen gameScreen) {
		this.g = g;
		this.gameScreen = gameScreen;
		landBought = false;
		elapsedTime = 0;
		turnTimer = new Timer(1000, null); // send event every 1000ms = 1s
		this.setCurrentPlayer(0);

		turnTimer.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent h) {

				if (elapsedTime < getMaxTime()) { // SHOULD BE elapsedTime <
													// maxTime.. but maxTime is
													// computed from round..
					elapsedTime++;
					gameScreen.updateTime(getMaxTime() - elapsedTime);
				}

				else {
					endTurn();
					turnTimer.stop();
				}

			}

		});
		//updatePlayerInfo();
		turnTimer.start(); // start ticking

	}

	/**
	 * Start turn activates the turn by starting the timer and setting the
	 * screen to Map.
	 */
	public void startTurn() {
		turnTimer.restart();
		landBought = false;
		elapsedTime = 0;
		updatePlayerInfo();
		gameScreen.getLayoutManager().show(gameScreen, "Map");
		gameScreen.getCounter().set(0);
		// display player turn message
	}

	/**
	 * End turn ends the turn by sending it to the pubview.
	 */
	public void endTurn() {
		turnTimer.stop();
		int winnings = gamble();
		PubView pubView = new PubView(g, winnings, gameScreen);
		gameScreen.getLayoutManager().removeLayoutComponent(pubView);
		setCurrentPlayer((getCurrentPlayer() + 1));

		pubView.btnStartNextRound.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent h) {
				System.out.println("I got clicked");
				System.out.println("The current player number is " + currentPlayer);
				if (gameScreen.getRound().getTurn().getCurrentPlayer() < 4) {
					gameScreen.getRound().getTurn().startTurn();
					
					gameScreen.refreshTownPresenter();
				} else {
					setCurrentPlayer((getCurrentPlayer())%4);
					gameScreen.getRound().endRound();
					gameScreen.refreshTownPresenter();
				}

			}
		});
		gameScreen.add(pubView, "pubView");
		//gameScreen.getLayoutManager().addLayoutComponent(pubView, "pubView");
		gameScreen.getLayoutManager().show(gameScreen, "pubView");
		gameScreen.getCounter().set(1);

	}

	/**
	 * getTimeBonus is a method that calculates and returns the timeBonus, which
	 * depends on the amount of time that's passed. This method is useful for
	 * gambling.
	 * 
	 * @return an integer that is the time bonus.
	 */
	public int getTimeBonus() {

		int timeRemaining = getMaxTime() - elapsedTime;

		if (timeRemaining >= 37) {
			return 200;
		}

		else if (timeRemaining >= 25) {
			return 150;
		}

		else if (timeRemaining >= 12) {
			return 100;
		}

		else if (timeRemaining == 0) {
			return 0;
		} // needed for gambling

		else {
			return 50;
		}

	}

	/**
	 * calculates the max time the player gets in his turn
	 * 
	 * @param foodNeeded
	 * @return int: the maxtime for the turn
	 */
	public int getMaxTime() {
		int foodNeeded = 0;
		if (g.getCurrentRound() < 5) {
			foodNeeded = 3;
		} else if (g.getCurrentRound() > 4 && g.getCurrentRound() < 9) {
			foodNeeded = 4;
		} else {
			foodNeeded = 5;
		}
		int playerFood = g.getPlayers()[getCurrentPlayer()].getFood();
		if (playerFood >= foodNeeded) {
			return 50;
		}

		else if (playerFood > 0) {
			return 30;

		}

		else {
			return 5;
		}

	}

	/**
	 * this method implements gambling. It increments the current player's funds
	 * by the random amount generated
	 * 
	 * @param currentRound
	 *            TODO: is currentRound a field for Turn?? Not sure how @MATT is
	 *            implementing this.
	 */
	public int gamble() {
		Random rand = new Random();
		int randomInt = rand.nextInt(getTimeBonus() + 1); // random integer
															// between 0 and
															// timeBonus [inc,
															// exc]
		int winnings = randomInt + getRoundBonus();

		System.out.println("Player "
				+ g.getPlayers()[getCurrentPlayer()].getName()
				+ " gambled and won: " + winnings);

		g.getPlayers()[getCurrentPlayer()].addMoney(winnings);
		updatePlayerInfo();

		System.out.println("Player "
				+ g.getPlayers()[getCurrentPlayer()].getName()
				+ "'s funds increased to: "
				+ g.getPlayers()[getCurrentPlayer()].getMoney());

		if (winnings < 250) { // gambling winnings are capped at 250
			return winnings;
		}

		else {
			return 250;
		}

	}

	/**
	 * Getter for time
	 * 
	 * @return time;
	 */
	public int getElapsedTime() {
		return elapsedTime;
	}

	/**
	 * Setter for time
	 * 
	 * @param time
	 */
	public void setTime(int time) {
		this.elapsedTime = time;
	}

	/**
	 * Getter for current player
	 * 
	 * @return currentPlayer
	 */
	public int getCurrentPlayer() {
		return currentPlayer;
	}

	/**
	 * Setter for current player
	 * 
	 * @param currentPlayer
	 */
	public void setCurrentPlayer(int currentPlayer) {
		this.currentPlayer = currentPlayer;
		System.out.println("Set player number to " + currentPlayer);
	}

	/**
	 * Stub class for the auction.
	 */
	public static void startAution() {

	}

	/**
	 * buyTile purchases the tile for the player that has an active turn.
	 */
	public void buyTile() {
		Player player = g.getPlayers()[currentPlayer];
		if (!landBought) {
			if (g.getMap()
					.getTile(gameScreen.getYCord(), gameScreen.getXCord())
					.getOwner() == null) {
				g.getMap()
						.getTile(gameScreen.getYCord(), gameScreen.getXCord())
						.buyTile(g.getPlayers()[currentPlayer],
								g.getCurrentRound());
				landBought = true;
			}

			else {
				JOptionPane.showMessageDialog(null,
						"That land is owned, please select another land.");
			}

		}

		else if (player.getPossessMule()) { // try emplacing a mule. NOTE: We
											// should try to factor this out.
			emplaceMule(player);

		}
	}

	/**
	 * places a mule on a tile, provided the player owns it and the mule and
	 * tile are 'compatible'
	 * 
	 */
	public void emplaceMule(Player player) {

		// get mule type and tile
		Mule mule = player.getMule();
		Tile emplacementTile = g.getMap().getTile(gameScreen.getYCord(),
				gameScreen.getXCord());

		// emplacement conditions
		boolean compatible = compatibleMuleTile(mule.getType(),
				emplacementTile.getType());
		boolean validOwner = (player == emplacementTile.getOwner());
		boolean muleAlreadyHere = emplacementTile.getMuleHere();

		// check if placement is valid, and if so emplace
		if (!muleAlreadyHere && compatible && validOwner) {

			emplacementTile.setMuleHere(true);
			emplacementTile.setMule(player.getMule());
			
			System.out.println("Mule emplaced succesfully!");
		}
		
		else {
			System.out.println("Mule Lost!");
		}

		// check in terminal
		System.out.println("Mule on this tile: "
				+ emplacementTile.getMuleHere());

		// player emplaces mule correctly or not. Either way, he loses
		// possession of it
		player.setPossessMule(false);
		System.out.println(player.getName() + " possesses a mule: "
				+ player.getPossessMule());

	}

	/**
	 * returns true if a mule of type muleType can produce on a tile of type
	 * tileType
	 * 
	 * @param muleType
	 *            : the mule type
	 * @param tileType
	 *            : the tile type
	 * @return
	 */
	public boolean compatibleMuleTile(String muleType, String tileType) {

		if (muleType == "Smithore" && tileType == "R") {
			return false;
		}

		else {
			return true;
		}
	}

	/**
	 * Getter for 'm' factor, which influences the money earned from gambling
	 * 
	 * @return int: round's 'm' factor
	 */
	public int getRoundBonus() {
		if (g.getCurrentRound() <= 3) {
			return 50;
		}

		else if (g.getCurrentRound() <= 7) {
			return 100;
		}

		else if (g.getCurrentRound() <= 11) {
			return 150;
		}

		else {
			return 200;
		}

	}
	
	/**
	 * Updates player info in the infoPanel
	 */
	public void updatePlayerInfo()
	{
		gameScreen.updatePlayer(g.getPlayers()[currentPlayer].getName());
		gameScreen.updateMoney(g.getPlayers()[currentPlayer].getMoney());
	}

	/**
	 * Returns whether land was bought.
	 * @return bought
	 */
	public boolean landBought() {
		return landBought;
	}
	
	/**
	 * Sets the boolean landBought.
	 * @param bought
	 */
	public void setLandBought(boolean bought) {
		landBought = bought;
	}
	
	/**
	 * Stops timer from running.
	 */
	public void stopTimer(){
		turnTimer.stop();
	}
}
